package com.atguigu.tingshu.user.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.WxMaUserService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.hash.Hash;
import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.album.impl.AlbumDegradeFeignClient;
import com.atguigu.tingshu.common.cache.GuiGuCache;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.user.*;
import com.atguigu.tingshu.user.mapper.*;
import com.atguigu.tingshu.user.service.UserInfoService;
import com.atguigu.tingshu.user.service.strategy.PaidRecordStrategy;
import com.atguigu.tingshu.user.service.strategy.factory.StrategyFactory;
import com.atguigu.tingshu.user.service.strategy.impl.AlbumPaidRecordStrategy;
import com.atguigu.tingshu.user.service.strategy.impl.TrackPaidRecordStrategy;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {

    @Autowired
    private UserInfoMapper userInfoMapper;

    @Autowired
    private WxMaService wxMaService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RabbitService rabbitService;

    @Autowired
    private AlbumFeignClient albumFeignClient;

    @Autowired
    private VipServiceConfigMapper vipServiceConfigMapper;

    /**
     * 微信一键登录
     *
     * @param code 前端提交来临时凭据
     * @return 登录成功用户token
     */
    @Override
    public Map<String, String> wxLogin(String code) {
        try {
            //1.java服务端对接微信登录SDK，获取微信用户账户唯一标识
            WxMaUserService userService = wxMaService.getUserService();
            WxMaJscode2SessionResult sessionInfo = userService.getSessionInfo(code);
            String wxOpenId = sessionInfo.getOpenid();

            //2.根据微信唯一标识wxOpenId 查询用户记录
            LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(UserInfo::getWxOpenId, wxOpenId);
            UserInfo userInfo = userInfoMapper.selectOne(queryWrapper);

            //3.判断如果微信唯一标识未绑定用户记录-说明首次登录
            if (userInfo == null) {
                //3.1 创建用户记录绑定微信唯一标识
                userInfo = new UserInfo();
                userInfo.setNickname("听友" + IdUtil.getSnowflakeNextId());
                userInfo.setAvatarUrl("https://guli-file-191130.oss-cn-beijing.aliyuncs.com/avatar/default.jpg");
                userInfo.setWxOpenId(wxOpenId);
                userInfoMapper.insert(userInfo);
                //3.2 发送初始化账户信息消息到RabbitMQ
                //3.2.1 封装初始化账户所需参数Map
                Map<String, Object> msgData = new HashMap<>();
                msgData.put("userId", userInfo.getId());
                msgData.put("title", "首次登录赠送体验金");
                msgData.put("amount", new BigDecimal("10"));
                msgData.put("orderNo", "zs" + IdUtil.getSnowflakeNextId());

                //3.2.2 发送可靠性消息
                rabbitService.sendMessage(MqConst.EXCHANGE_USER, MqConst.ROUTING_USER_REGISTER, msgData);
            }
            //4.登录成功，生成token
            //4.1 生成token
            String token = IdUtil.randomUUID();
            String loginKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token;
            //4.2 将用户信息转为用户基本信息UserInfoVo
            UserInfoVo userInfoVo = BeanUtil.copyProperties(userInfo, UserInfoVo.class);
            //4.3 将用户基本信息存入redis
            redisTemplate.opsForValue().set(loginKey, userInfoVo, RedisConstant.USER_LOGIN_KEY_TIMEOUT, TimeUnit.SECONDS);
            //4.4 封装令牌相应给客户端
            Map<String, String> map = new HashMap<>();
            map.put("token", token);
            return map;
        } catch (Exception e) {
            log.error("[用户服务]微信登录异常：{}", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取指定用户信息
     *
     * @return
     */
    @Override
    @GuiGuCache(prefix = "user:info:")
    public UserInfoVo getUserInfo(Long userId) {
        UserInfo userInfo = userInfoMapper.selectById(userId);
        return BeanUtil.copyProperties(userInfo, UserInfoVo.class);
    }


    /**
     * 更新用户基本信息
     *
     * @param userInfoVo
     */
    @Override
    public void updateUser(UserInfoVo userInfoVo) {
        Long userId = AuthContextHolder.getUserId();
        UserInfo userInfo = new UserInfo();
        userInfo.setId(userId);
        userInfo.setNickname(userInfoVo.getNickname());
        userInfo.setAvatarUrl(userInfoVo.getAvatarUrl());
        userInfoMapper.updateById(userInfo);
    }

    @Autowired
    private UserPaidAlbumMapper userPaidAlbumMapper;

    @Autowired
    private UserPaidTrackMapper userPaidTrackMapper;

    @Autowired
    private UserVipServiceMapper userVipServiceMapper;

    /**
     * 判断提交声音ID购买状态
     *
     * @param userId                        用户ID
     * @param albumId                       专辑ID
     * @param needCheckPayStatusTrackIdList 需要检查的购买状态的声音ID列表
     * @return {声音ID:购买状态,声音ID：购买状态} 购买状态：1=已购买 0=未购买
     */
    @Override
    public Map<Long, Integer> userIsPaidTrack(Long userId, Long albumId, List<Long> needCheckPayStatusTrackIdList) {
        Map<Long, Integer> payStatusMap = new HashMap<>();
        //1.尝试根据用户ID+专辑ID查询已购专辑表判断是否购买过专辑
        LambdaQueryWrapper<UserPaidAlbum> paidAlbumLambdaQueryWrapper = new LambdaQueryWrapper<>();
        paidAlbumLambdaQueryWrapper.eq(UserPaidAlbum::getUserId, userId);
        paidAlbumLambdaQueryWrapper.eq(UserPaidAlbum::getAlbumId, albumId);
        paidAlbumLambdaQueryWrapper.last("limit 1");
        Long count = userPaidAlbumMapper.selectCount(paidAlbumLambdaQueryWrapper);

        //2.如果购买专辑，则将提交的声音ID列表全部标记为已购买=1返回 结束
        if (count > 0) {
            for (Long trackId : needCheckPayStatusTrackIdList) {
                payStatusMap.put(trackId, 1);
            }
            return payStatusMap;
        }
        //3.如果未购买专辑，根据用户ID+专辑ID查询已购声音列表
        LambdaQueryWrapper<UserPaidTrack> paidTrackLambdaQueryWrapper = new LambdaQueryWrapper<>();
        paidTrackLambdaQueryWrapper.eq(UserPaidTrack::getUserId, userId);
        paidTrackLambdaQueryWrapper.eq(UserPaidTrack::getAlbumId, albumId);
        paidTrackLambdaQueryWrapper.select(UserPaidTrack::getTrackId);
        List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(paidTrackLambdaQueryWrapper);

        //4.如果已购声音列表为空，则将提交的声音ID列表全部标记为未购买=0返回 结束
        if (CollectionUtil.isEmpty(userPaidTrackList)) {
            for (Long trackId : needCheckPayStatusTrackIdList) {
                payStatusMap.put(trackId, 0);
            }
            return payStatusMap;
        }

        //5.如果已购声音列表有值，遍历待检查声音ID列表判断哪些已购哪些未购买 返回 结束
        List<Long> userPaidTrackIdList = userPaidTrackList.stream().map(UserPaidTrack::getTrackId).collect(Collectors.toList());
        for (Long needCheckPayStatusTrackId : needCheckPayStatusTrackIdList) {
            //如果已购声音ID集合中包含待检查声音ID 将购买状态标记为已购买=1
            if (userPaidTrackIdList.contains(needCheckPayStatusTrackId)) {
                payStatusMap.put(needCheckPayStatusTrackId, 1);
            } else {
                //反之标记为未购买=0
                payStatusMap.put(needCheckPayStatusTrackId, 0);
            }
        }
        return payStatusMap;
    }

    /**
     * 判断当前用户是否购买过指定专辑
     *
     * @param albumId
     * @return
     */
    @Override
    public Boolean isPaidAlbum(Long albumId) {
        //1.获取当前用户ID
        Long userId = AuthContextHolder.getUserId();
        //2.根据用户ID+专辑ID查询已购专辑表
        LambdaQueryWrapper<UserPaidAlbum> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserPaidAlbum::getUserId, userId);
        queryWrapper.eq(UserPaidAlbum::getAlbumId, albumId);
        Long count = userPaidAlbumMapper.selectCount(queryWrapper);
        return count > 0;
    }

    /**
     * 获取用户已购声音ID列表
     *
     * @param userId
     * @param albumId
     * @return
     */
    @Override
    public List<Long> getUserPaidTrackIdList(Long userId, Long albumId) {
        //1.构建查询条件
        LambdaQueryWrapper<UserPaidTrack> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserPaidTrack::getUserId, userId);
        queryWrapper.eq(UserPaidTrack::getAlbumId, albumId);
        queryWrapper.select(UserPaidTrack::getTrackId);
        List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(queryWrapper);
        //2.获取已购声音列表，获取已购声音ID集合
        if (CollectionUtil.isNotEmpty(userPaidTrackList)) {
            List<Long> userPaidTrackIdList = userPaidTrackList.stream()
                    .map(UserPaidTrack::getTrackId)
                    .collect(Collectors.toList());
            return userPaidTrackIdList;

        }
        return null;
    }

    @Autowired
    private StrategyFactory strategyFactory;

    /**
     * 用户付款成功(余额，微信)后，给用户进行虚拟物品(VIP会员，专辑，声音)发货
     *
     * @param userPaidRecordVo
     * @return
     */
    @Override
    public void savePaidRecord(UserPaidRecordVo userPaidRecordVo) {
        String itemType = userPaidRecordVo.getItemType();
        //1.根据货物类型从工厂中获取实现类对象
        PaidRecordStrategy strategy = strategyFactory.getStrategyImpl(itemType);
        //2.调用对应实现类对象发货逻辑
        strategy.handlerPaidRecord(userPaidRecordVo);
    }

    /**
     * 更新VIP状态：处理过期会员
     * @return
     */
    @Override
    public void updateVipExpireStatus(Date date) {
        //1.查询所有已过期的VIP用户
        LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserInfo::getIsVip, 1);
        queryWrapper.lt(UserInfo::getVipExpireTime, date);
        queryWrapper.select(UserInfo::getId);
        List<UserInfo> userInfoList = userInfoMapper.selectList(queryWrapper);
        //2.更新用户会员标识：从1改为0
        if(CollectionUtil.isNotEmpty(userInfoList)){
            log.info("[用户服务]获取到过期会员列表，执行更新会员标识业务");
            //for (UserInfo userInfo : userInfoList) {
            //    userInfo.setIsVip(0);
            //    userInfoMapper.updateById(userInfo);
            //}
            userInfoList.stream().forEach(u->u.setIsVip(0));
            this.updateBatchById(userInfoList);
        }
    }

}
